package com.ncmem.up7.controller;

import com.google.gson.Gson;
import com.ncmem.up7.*;
import com.ncmem.up7.biz.DbFolder;
import com.ncmem.up7.biz.PathBuilder;
import com.ncmem.up7.biz.PathGuidBuilder;
import com.ncmem.up7.biz.folder.fd_scan;
import com.ncmem.up7.biz.up7_biz_event;
import com.ncmem.up7.model.FileInf;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;

/**
 * * Created by jmzy on 2021/3/5.
 * 接口：
 * up7/clear
 * up7/f_create
 * up7/f_post
 * up7/f_process
 * up7/f_complete
 * up7/f_del
 * up7/f_list
 * up7/fd_create
 * up7/fd_complete
 * up7/fd_del
 * 注意：
 * 1.除f_post外其它接口均由前端(up7.js,up7.file.js,up7.folder.js)自动调用
 * 2.f_post由控件自动调用。
 */
@RestController
public class Up7Api {

    @GetMapping("up7/test")
    public String test() {
        ClassPathResource res = new ClassPathResource("upload");
        try {
            return res.getFile().getPath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 清空数据库，由前端调用
     * @return
     */
    @RequestMapping(value="up7/clear",method = RequestMethod.GET)
    public String clear()
    {
        DBConfig cfg = new DBConfig();
        cfg.db().Clear();
        return "数据库清理完毕";
    }

    /**
     * 文件初始化,在文件上传前调用，在数据表中记录文件信息。由前諯调用
     * 调用位置：up7.file.js-md5_complete
     * @param id 文件ID，由控件生成，GUID     *
     * @param uid，用户ID，在JS中配置
     * @param lenLoc，数字格式的文件大小，控件提供
     * @param sizeLoc 格式化的文件大小，控件提供
     * @param blockCount 块总数，控件提供
     * @param blockSize 块大小，控件提供
     * @param callback JQ回调方法名称
     * @param pathLoc 文件本地路径，控件提供
     * @return
     */
    @RequestMapping(value="up7/f_create",method = RequestMethod.GET)
    public String f_create(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="0")Integer uid,
                           @RequestParam(value="lenLoc", required=false,defaultValue="0")Long lenLoc,
                           @RequestParam(value="sizeLoc", required=false,defaultValue="")String sizeLoc,
                           @RequestParam(value="blockSize", required=false,defaultValue="0")Integer blockSize,
                           @RequestParam(value="blockCount", required=false,defaultValue="0")Integer blockCount,
                           @RequestParam(value="callback", required=false,defaultValue="")String callback,
                           @RequestParam(value="pathLoc", required=false,defaultValue="")String pathLoc)
    {

        pathLoc	= PathTool.url_decode(pathLoc);

        //参数为空
        if ( StringUtils.isBlank(sizeLoc) )
        {
            return (callback + "({\"value\":null})");
        }

        FileInf fileSvr= new FileInf();
        fileSvr.id = id;
        fileSvr.uid = uid;
        fileSvr.nameLoc = PathTool.getName(pathLoc);
        fileSvr.pathLoc = pathLoc;
        fileSvr.lenLoc = lenLoc;
        fileSvr.sizeLoc = sizeLoc;
        fileSvr.deleted = false;
        fileSvr.nameSvr = fileSvr.nameLoc;

        //所有单个文件均以uuid/file方式存储
        PathGuidBuilder pb = new PathGuidBuilder();
        try {
            fileSvr.pathSvr = pb.genFile(fileSvr);
        } catch (IOException e) {
            e.printStackTrace();
        }

        DBConfig cfg = new DBConfig();
        DBFile db = cfg.db();
        db.add(fileSvr);
        up7_biz_event.file_create(fileSvr);

        Gson gson = new Gson();
        String json = gson.toJson(fileSvr);
        json = PathTool.url_encode(json);
        json = callback + "({\"value\":\"" + json + "\"})";//返回jsonp格式数据给前端。
        return json;
    }

    /**
     * 文件上传完毕，在文件上传完毕后由前端调用。
     * 调用位置：up7.file.js-post_complete
     * @param id 文件ID，由控件生成，GUID 示例：04fdfc13fc2a4b7a9d0682f8c344926f
     * @param uid 用户ID，在JS中提供
     * @param callback JQ回调,前端提供
     * @return
     */
    @RequestMapping(value="up7/f_complete",method = RequestMethod.GET)
    public String f_complete(@RequestParam(value="id", required=false,defaultValue="")String id,
                             @RequestParam(value="uid", required=false,defaultValue="0")Integer uid,
                             @RequestParam(value="callback", required=false,defaultValue="")String callback)
    {
        //返回值。1表示成功
        int ret = 0;
        if ( !StringUtils.isBlank(id))
        {
            DBConfig cfg = new DBConfig();
            DBFile db = cfg.db();
            db.complete(id);
            db.merged(id);

            up7_biz_event.file_post_complete(id);
            ret = 1;
        }
        return callback + "(" + ret + ")";
    }

    /**
     * 删除文件,在文件列表中调用，由前端调用。
     * 调用位置，up7.js
     * @param id 文件ID
     * @param uid 用户ID
     * @param callback JQ回调方法，用于支持跨域调用
     * @return
     */
    @RequestMapping(value="up7/f_del",method = RequestMethod.GET)
    @ResponseBody
    public String f_del(@RequestParam(value="id", required=false,defaultValue="")String id,
                        @RequestParam(value="uid", required=false,defaultValue="0")Integer uid,
                        @RequestParam(value="callback", required=false,defaultValue="")String callback
    )
    {
        //返回值。1表示成功
        int ret = 0;

        if (!StringUtils.isBlank(id) )
        {
            DBConfig cfg = new DBConfig();
            DBFile db = cfg.db();
            db.remove(id,uid);
            up7_biz_event.file_del(id,uid);
            ret = 1;
        }
        return callback + "(" + ret + ")";
    }

    /**
     * 加载未上传完的文件列表，在文件列表中调用，由前端调用。
     * 加载上传面板时自动调用。
     * 调用位置：up7.js
     * @param uid 用户UI
     * @param cbk JQ回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="up7/f_list",method = RequestMethod.GET)
    public String f_list(@RequestParam(value="uid", required=false,defaultValue="0")Integer uid,
                         @RequestParam(value="callback", required=false,defaultValue="")String cbk
    )
    {
        DBConfig cfg = new DBConfig();
        String json = cfg.db().all_uncmp( uid );
        if( !StringUtils.isBlank(json))
        {
            try {
                json = URLEncoder.encode(json,"utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            json = json.replace("+","%20");
            return  cbk + "({\"value\":\""+json + "\"})" ;

        }
        return (cbk + "({\"value\":null})");
    }

    /**
     * 上传文件块，由控件调用
     * @param uid 用户ID
     * @param id 文件ID
     * @param pid 父级ID，由控件提供
     * @param pidRoot 根ID，由控件提供
     * @param lenSvr 远程文件大小，由控件提供
     * @param lenLoc 本地文件大小，由控件提供
     * @param sizeLoc 格式化的文件大小，由控件提供
     * @param blockOffset 块偏移位置（相对于整个文件），由控件提供
     * @param blockSize 块大小，由控件提供
     * @param blockIndex 块索引，基于1，由控件提供
     * @param blockMd5 块MD5，由控件提供
     * @param pathSvr 远程文件路径（文件保存位置），由控件提供
     * @param blockData 块数据，由控件提供
     * @param blockCount 块总数，由控件提供
     * @param blockSizeLogic 块逻辑大小，在js中定义
     * @param nameLoc 文件本地名称，由控件提供
     * @param pathLoc 文件本地路径，由控件提供
     * @param pathRel 文件相对路径，由控件提供
     * @return
     */
    @RequestMapping(value="up7/f_post",method = RequestMethod.POST)
    public String f_post(@RequestHeader(value="uid", required=false,defaultValue="0")Integer uid,
                         @RequestHeader(value="id", required=false,defaultValue="")String id,
                         @RequestHeader(value="pid", required=false,defaultValue="")String pid,
                         @RequestHeader(value="pidRoot", required=false,defaultValue="")String pidRoot,
                         @RequestHeader(value="lenSvr", required=false,defaultValue="0")Long lenSvr,
                         @RequestHeader(value="lenLoc", required=false,defaultValue="0")Long lenLoc,
                         @RequestHeader(value="nameLoc", required=false,defaultValue="")String nameLoc,
                         @RequestHeader(value="sizeLoc", required=false,defaultValue="")String sizeLoc,
                         @RequestHeader(value="blockOffset", required=false,defaultValue="0")Long blockOffset,
                         @RequestHeader(value="blockIndex", required=false,defaultValue="0")Integer blockIndex,
                         @RequestHeader(value="blockCount", required=false,defaultValue="0")Integer blockCount,
                         @RequestHeader(value="blockSize", required=false,defaultValue="0")Integer blockSize,
                         @RequestHeader(value="blockSizeLogic", required=false,defaultValue="0")Integer blockSizeLogic,
                         @RequestHeader(value="blockMd5", required=false,defaultValue="")String blockMd5,
                         @RequestHeader(value="pathLoc",required=false,defaultValue="") String pathLoc,
                         @RequestHeader(value="pathRel",required=false,defaultValue="") String pathRel,
                         @RequestHeader(value="pathSvr",required=false,defaultValue="") String pathSvr,
                         @RequestParam(value="file",required=true) MultipartFile blockData
    )
    {
        //参数为空
        if(
                StringUtils.isBlank( id ) ||
                StringUtils.isBlank(nameLoc) ||
                StringUtils.isBlank(pathLoc) )
        {
            return "uid nameLoc pathLoc is empty";
        }
        pathSvr = PathTool.url_decode(pathSvr);

        //块大小不正确
        if(blockData.getSize() != blockSize)
        {
            return "blockSize error";
        }

        //计算块MD5
        String md5Svr="";
        if( !StringUtils.isBlank(blockMd5) ) {
            try {
                md5Svr = Md5Tool.fileToMD5(blockData.getInputStream() );
            } catch (IOException e) {
                e.printStackTrace();
            }

            //块MD5验证
            if(!md5Svr.equals(blockMd5))
            {
                return "block md5 error";
            }
        }

        //保存块
        FileBlockWriter fbw = new FileBlockWriter();
        if(blockIndex==1) fbw.make(pathSvr,lenSvr);
        fbw.write(pathSvr, lenLoc, blockOffset, blockData);

        //触发事件
        up7_biz_event.file_post_block(id, blockIndex);

        //返回信息
        JSONObject res = new JSONObject();
        res.put("msg", "ok");
        res.put("md5", md5Svr);
        res.put("offset", blockOffset);//基于文件的块偏移位置
        return res.toString();
    }

    /**
     * 更新文件进度，由前端调用。
     * 调用位置：up7.file.js
     * @param id 文件ID
     * @param uid 用户ID
     * @param offset 偏移位置
     * @param lenSvr 已传大小
     * @param perSvr 已传百分比
     * @param cbk JQ回调方法
     * @return
     */
    @RequestMapping(value="up7/f_process",method = RequestMethod.GET)
    public String f_process(
            @RequestParam(value="id", required=false,defaultValue="")String id,
            @RequestParam(value="uid", required=false,defaultValue="")String uid,
            @RequestParam(value="offset", required=false,defaultValue="")String offset,
            @RequestParam(value="lenSvr", required=false,defaultValue="")String lenSvr,
            @RequestParam(value="perSvr", required=false,defaultValue="")String perSvr,
            @RequestParam(value="callback", required=false,defaultValue="")String cbk
    )
    {
        int ret = 0;
        if (	!StringUtils.isBlank(id)
                &&	!StringUtils.isBlank(lenSvr)
                &&	!StringUtils.isBlank(perSvr))
        {
            DBConfig cfg = new DBConfig();
            DBFile db = cfg.db();
            //db.f_process(Integer.parseInt(uid),id,Long.parseLong(offset),Long.parseLong(lenSvr),perSvr);
            up7_biz_event.file_post_process(id);
            ret = 1;
        }
        return cbk + "({\"value\":"+ret+"})";
    }

    /**
     * 文件夹上传完毕，在文件夹上传完毕后自动调用。由前端调用
     * 调用位置：up7.folder.js
     * @param id 文件ID
     * @param uid 用户ID
     * @param cbk JQ回调方法
     * @return
     */
    @RequestMapping(value="up7/fd_complete",method = RequestMethod.GET)
    public String fd_complete(
            @RequestParam(value="id", required=false,defaultValue="")String id,
            @RequestParam(value="uid", required=false,defaultValue="0")Integer uid,
            @RequestParam(value="callback", required=false,defaultValue="")String cbk,
            @RequestParam(value="cover", required=false,defaultValue="0")Integer cover
    ) throws IOException, SQLException {
        int ret = 0;

        //参数为空
        if (	!StringUtils.isBlank(id) )
        {
            //取当前节点信息
            DBConfig cfg = new DBConfig();
            DBFile db = cfg.db();
            db.complete(id);

            FileInf folder = new FileInf();
            folder.id = id;
            db.read(folder);

            //扫描文件夹
            fd_scan sc = new fd_scan();
            sc.scan(folder);

            db.merged(id);
            ret = 1;
            up7_biz_event.folder_post_complete(id);

            ret = 1;
        }
        return cbk + "(" + ret + ")";
    }

    /**
     * 文件夹初始化，在上传文件夹时自动调用。
     * 调用位置：up7.folder.js
     * @param id 文件夹ID，GUID格式
     * @param uid 用户ID
     * @param lenLoc 数字化的文件夹大小：1024
     * @param sizeLoc 格式化的文件夹大小：10MB
     * @param pathLoc 文件夹本地路径。
     * @param fileCount 文件总数
     * @param cbk JQ回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="up7/fd_create",method = RequestMethod.GET)
    public String fd_create(
            @RequestParam(value="id", required=false,defaultValue="")String id,
            @RequestParam(value="uid", required=false,defaultValue="")String uid,
            @RequestParam(value="lenLoc", required=false,defaultValue="")String lenLoc,
            @RequestParam(value="sizeLoc", required=false,defaultValue="")String sizeLoc,
            @RequestParam(value="pathLoc", required=false,defaultValue="")String pathLoc,
            @RequestParam(value="filesCount", required=false,defaultValue="")String fileCount,
            @RequestParam(value="callback", required=false,defaultValue="")String cbk
    )
    {
        //参数为空
        if (
                StringUtils.isBlank(id)||
                        StringUtils.isBlank(uid)||
                        StringUtils.isBlank(pathLoc))
        {
            return (cbk + "({\"value\":null})");
        }
        pathLoc = PathTool.url_decode(pathLoc);


        FileInf fileSvr = new FileInf();
        fileSvr.id      = id;
        fileSvr.fdTask  = true;
        fileSvr.uid     = Integer.parseInt(uid);
        fileSvr.nameLoc = PathTool.getName(pathLoc);
        fileSvr.pathLoc = pathLoc;
        fileSvr.lenLoc  = Long.parseLong(lenLoc);
        fileSvr.fileCount = Integer.parseInt(fileCount);
        fileSvr.sizeLoc = sizeLoc;
        fileSvr.deleted = false;
        fileSvr.nameSvr = fileSvr.nameLoc;

        //生成存储路径
        PathGuidBuilder pb = new PathGuidBuilder();
        try {
            fileSvr.pathSvr = pb.genFolder(fileSvr.uid,fileSvr.id, fileSvr.nameLoc);
        } catch (IOException e) {
            e.printStackTrace();
        }
        fileSvr.pathSvr = fileSvr.pathSvr.replace("\\","/");
        PathTool.createDirectory(fileSvr.pathSvr);

        //添加到数据表
        DBConfig cfg = new DBConfig();
        DBFile db = cfg.db();
        db.add(fileSvr);

        //添加到目录表
        DBFolder fd = cfg.folderDB();
        fd.add(fileSvr);

        up7_biz_event.folder_create(fileSvr);

        Gson g = new Gson();
        String json = g.toJson(fileSvr);
        json = PathTool.url_encode(json);

        JSONObject ret = new JSONObject();
        ret.put("value",json);
        json = cbk + String.format("(%s)",ret.toString());//返回jsonp格式数据。
        return json;
    }

    /**
     * 删除文件夹，在文件列表中点击删除按钮时调用，由前端调用
     * 调用位置：up7.js
     * @param id 文件夹ID
     * @param uid 用户ID
     * @param cbk JQ回调方法，提供跨域调用
     * @return
     */
    @RequestMapping(value="up7/fd_del",method = RequestMethod.GET)
    public String fd_del(
            @RequestParam(value="id", required=false,defaultValue="")String id,
            @RequestParam(value="uid", required=false,defaultValue="0")Integer uid,
            @RequestParam(value="callback", required=false,defaultValue="")String cbk
    )
    {
        int ret = 0;
        //参数为空
        if (	!StringUtils.isBlank(id) )
        {
            DBConfig cfg = new DBConfig();
            DBFile db = cfg.db();
            DBFolder fd = cfg.folderDB();

            db.remove(id,uid);//删除文件表数据
            fd.remove(id,uid);//删除目录表数据
            ret = 1;
        }
        return cbk + "({\"value\":"+ret+"})";
    }
}
